package top.yfq.shortlinkweb.util;

import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Component;
import top.yfq.shortlinkweb.mapper.IdSegmentMapper;
import top.yfq.shortlinkweb.model.po.IdSegmentPO;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicLong;

/**
 * @author yfq
 * @date 2025/7/18 16:14
 * @description 分段ID生成器
 */
@Component
public class IdSegmentGenerator {
    private volatile long maxId;
    private final AtomicLong currentId = new AtomicLong(0);
    private final ExecutorService loader = Executors.newSingleThreadExecutor();

    @Resource
    private IdSegmentMapper idSegmentMapper;

    @PostConstruct
    public void init() {
        loadSegment();
        // 异步监控号段的使用率，超过80%被使用时，重新加载号段
        loader.submit(this::daemonLoad);
    }

    private void loadSegment() {
        boolean flag;
        IdSegmentPO idSegmentPO;
        do {
            // 从DB获取号段
            idSegmentPO = idSegmentMapper.queryIdSegment("short_url");

            // 每次取step个号
            this.maxId = idSegmentPO.getMaxId() + idSegmentPO.getStep();

            // 设置maxId到数据库
            flag = idSegmentMapper.setMaxId("short_url", maxId, idSegmentPO.getVersion());
        } while (!flag);

        currentId.set(idSegmentPO.getMaxId());
    }

    private void daemonLoad() {
        // 号段使用80%时异步加载
        while (currentId.get() > maxId * 0.8) {
            loadSegment();
            try {
                Thread.sleep(100);
            } catch (InterruptedException ignore) {
                Thread.currentThread().interrupt();
                break;
            }
        }
    }

    public long nextId() {
        if (currentId.get() >= maxId) throw new RuntimeException("ID segment is exhausted.");
        return currentId.incrementAndGet();
    }
}
